__author__ = 'cain76'

import os
import sys

sys.path.insert(0, os.path.expanduser('~') + '/lib/python')

from mylib.gl3d.light import *
from mylib.gl3d.camera import *
from mylib.gl3d.font import *
from mylib.gl3d.texture import *
from mylib.game.gamemap import *
from mylib.gl3d.shader import *
from mylib.gl3d.drawobject import *

SCREEN_SIZE = (600, 800)
mouse_drag = 0
mouse_last_pos = 0, 0
mouse_down_pos = 0, 0
mouse_select_index = 0

# The function called when our window is resized (which shouldn't happen if you enable fullscreen, below)
def glResize3D(width, height):
    # Prevent A Divide By Zero If The Window Is Too Small
    if height < 1: height = 1

    glViewport(0, 0, width, height)  # Reset The Current Viewport And Perspective Transformation
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()

    # // field of view, aspect ratio, near and far
    # This will squash and stretch our objects as the window is resized.
    # gluPerspective(45.0, float(width) / float(height), 0.0, 5000.0)
    gluPerspective(45.0, float(width) / float(height), 0.01, 500.0)

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()


def glResize2D(width, height):
    # Prevent A Divide By Zero If The Window Is Too Small
    if height < 1: height = 1

    glViewport(0, 0, width, height)  # Reset The Current Viewport And Perspective Transformation
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()

    # // Establish clipping volume (left, right, bottom, top, near, far)
    # glOrtho(0, width, 0, height, 0.0, 5000.0)

    gluOrtho2D(0, 0 + width, 0, 0 + height)

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()


def check_camera_move(event, camera, game):
    global mouse_drag, mouse_last_pos, mouse_down_pos, mouse_select_index
    if event.type == MOUSEMOTION:  # {
        if mouse_drag in (2, 3):  # {
            mx, my = event.pos
            lx, ly = mouse_last_pos
            dx, dy = (mx - lx), (my - ly)
            mouse_last_pos = mx, my
            if mouse_drag == 3: camera.change_view(dy / 10.0, dx / 10.0)
            elif mouse_drag == 2: camera.move(_x=dx / 10.0, _z=dy / 10.0)
            # print_camera_info(camera)
            # }
    # }
    if event.type == MOUSEBUTTONDOWN:  # {
        if event.button in (2, 3):  # {
            mouse_last_pos = event.pos
            mouse_drag = event.button
        # }
        elif event.button == 4: camera.distance *= 0.8
        elif event.button == 5: camera.distance *= 1.2
        elif event.button == 1:  # {
            mx, my = event.pos
            mouse_down_pos = mx, my
            mouse_select_index = game.check_select(camera, mx, my)
        # }

        # print(event.button)
        # print_camera_info(camera)
    # }
    if event.type == MOUSEBUTTONUP:
        if event.button in (2, 3): mouse_drag = 0
        elif event.button == 1:  # {
            mx, my = event.pos
            idx = game.check_select(camera, mx, my)
            game.swap(idx, mouse_select_index)
        # }


def print_camera_info(cam):
    print('rotate = %5.2f, angle = %5.2f, distance = %8.3f, '
          'camera = (%5.2f,%5.2f,%5.2f), target = (%5.2f,%5.2f,%5.2f)' % (
              cam.rotate, cam.angle, cam.distance, cam.camera.x, cam.camera.y, cam.camera.z, cam.target.x, cam.target.y,
              cam.target.z))


def get_light_pos(cam):
    dist = cam.distance * 1.5
    p1 = cam.get_position(cam.target, distance=dist)
    # p1 = cam.camera

    d = Vec3()
    d.x = -math.sin(math.radians(cam.rotate + 90))
    d.y = -math.tan(math.radians(cam.angle))
    d.z = math.cos(math.radians(cam.rotate + 90))
    return p1, d


def get_shader_src():
    return """
    vec3 ambient_light = light_ambient(light1).rgb+
                         light_ambient(light2).rgb+
                         light_ambient(light3).rgb+
                         light_ambient(light4).rgb+
                         light_ambient(light5).rgb+
                         light_ambient(light6).rgb+
                         light_ambient(light7).rgb+
                         light_ambient(light8).rgb;

    vec3 diffuse_light = light_diffuse(light1).rgb*light_att(light1)+
                     2.0*light_diffuse(light2).rgb*light_att(light2)*light_spot(light2)+
                         light_diffuse(light3).rgb*light_att(light3)+
                     2.0*light_diffuse(light4).rgb*light_att(light4)*light_spot(light4)+
                         light_diffuse(light5).rgb*light_att(light5)+
                     2.0*light_diffuse(light6).rgb*light_att(light6)*light_spot(light6)+
                         light_diffuse(light7).rgb*light_att(light7)+
                     2.0*light_diffuse(light8).rgb*light_att(light8)*light_spot(light8);

    vec3 specular_light = light_specular_ph(light1).rgb*light_att(light1)+
                      2.0*light_specular_ph(light2).rgb*light_att(light2)*light_spot(light2)+
                          light_specular_ph(light3).rgb*light_att(light3)+
                      2.0*light_specular_ph(light4).rgb*light_att(light4)*light_spot(light4)+
                          light_specular_ph(light5).rgb*light_att(light5)+
                      2.0*light_specular_ph(light6).rgb*light_att(light6)*light_spot(light6)+
                          light_specular_ph(light7).rgb*light_att(light7)+
                      2.0*light_specular_ph(light8).rgb*light_att(light8)*light_spot(light8);

    color.rgb += ambient_color.rgb*ambient_light;
    color.rgb += diffuse_color.rgb*diffuse_light;
    color.rgb += specular_color.rgb*specular_light;
    color.rgba *= texture2D(tex2D_1,uv).rgba; // texture color
    """


def random_color(alpha=True, bytefloat='b'):
    r = random.randint(0, 255)
    g = random.randint(0, 255)
    b = random.randint(0, 255)
    if alpha:
        a = random.randint(0, 255)
        if bytefloat == 'b': return r, g, b, a
        else: return r / 255.0, g / 255.0, b / 255.0, a / 255.0
    else:
        if bytefloat == 'b': return r, g, b
        else: return r / 255.0, g / 255.0, b / 255.0


def DrawText(font, xx, yy, zz, tt):
    glColor4fv(random_color())
    glMatrixMode(GL_PROJECTION)  # // Select The Projection Matrix
    glPushMatrix()  # // Store The Projection Matrix
    glLoadIdentity()  # // Reset The Projection Matrix
    glResize2D(SCREEN_SIZE[0], SCREEN_SIZE[1])
    glMatrixMode(GL_MODELVIEW)  # // Select The Modelview Matrix
    glPushMatrix()  # // Store The Modelview Matrix
    glLoadIdentity()  # // Reset The Modelview Matrix

    font.glPrint(xx, yy, zz, tt)

    glMatrixMode(GL_PROJECTION)  # // Select The Projection Matrix
    glPopMatrix()  # // Restore The Old Projection Matrix
    glMatrixMode(GL_MODELVIEW)  # // Select The Modelview Matrix
    glPopMatrix()  # // Restore The Old Projection Matrix
    # glResize3D(SCREEN_SIZE[0], SCREEN_SIZE[1])


def run():
    usr_home = os.path.expanduser('~')
    data_home = usr_home + '/src/res/'

    pygame.init()
    screen = pygame.display.set_mode(SCREEN_SIZE, HWSURFACE | OPENGL | DOUBLEBUF)

    glInit(depth=True, cull=True, blend=True, clear=(0.2, 0.2, 0.2, 1.0))
    # glInitFog()
    glResize3D(SCREEN_SIZE[0], SCREEN_SIZE[1])

    bullet = []

    font = None
    font = glFont()
    # font.BuildFont(data_home + 'fonts/Gulim16.png', wchars=256, hchars=256, scale=1.0)
    font.BuildFont(data_home + 'fonts/font8x16.png', scale=1.0)

    # ===================================================================================== Texture
    tex_file = None
    map_list = ['ammoc01.jpg', 'crate2.jpg', "heightmap/texture.jpg", "heightmap/box1.bmp", "heightmap/box.bmp",
                "heightmap/H002.bmp", "heightmap/H005.bmp", "heightmap/Earth.bmp", "heightmap/H000.bmp"]
    map_file = map_list[2]
    if tex_file is None: tex_file = map_file

    textures = Textures(lib=Textures.PIL)
    textures.load('tex', data_home + tex_file)
    textures.load('box', data_home + map_list[1])
    textures.load('block', data_home + 'Hexa/block0.png')
    for i in range(9):
        gem = 'block' + str(i + 1)
        _, cnt = textures.load(gem, data_home + 'Hexa/' + gem + '.png', alpha=False)

    # ===================================================================================== GameMap
    gametype = GameMap.GT_MATCH
    game_map = GameMap(game_type=gametype)
    GameCell.use_multi_tex = 0
    game_map.set_texture(11, textures.get('box'))
    game_map.set_texture(1, textures.get('block'))
    for i in range(9):
        gem = 'block' + str(i + 1)
        game_map.set_texture(20 + i, textures.get(gem))

    if gametype != GameMap.GT_MATCH:
        if gametype == GameMap.GT_TETRIS: itm = TetrisItem()
        elif gametype == GameMap.GT_HEXA: itm = HexaItem()
        elif gametype == GameMap.GT_PUYO: itm = TetrisItem()
        elif gametype == GameMap.GT_SAME: itm = TetrisItem()
        else: itm = TetrisItem()

        itm = itm.get_next()

        itm.set_pos(game_map.get_centeri()[0], 0, game_map.get_top() + 4)
        itm.set_dst(game_map.get_centeri()[0], 0, game_map.get_top())

    # ===================================================================================== Light
    light1 = None
    light1 = Light3d(1)
    light1.set_type(MYGL_POINT_LIGHT)
    light1.set_diffuse([0.9, 0.9, 0.9])
    light1.enable()

    # ===================================================================================== Shader
    shader = None

    shader = GLShader()
    shader.render_equation(get_shader_src())
    errors = shader.compile()
    print('Shader Load Check - ', errors)

    # ===================================================================================== Camera
    gcw, gch = game_map.get_centerf()
    gcw, gch = gcw - 0.5, gch - 1.0
    camera = Camera(Camera.CT_CAMERA)
    camera.set_target(gcw, 0, gch)
    camera.distance = 25
    camera.angle = 60.0
    camera.rotate = 270.0

    do = DrawObject()
    clock = pygame.time.Clock()
    del_cnt = 1
    score = 0
    del_seconds = 0.0

    while True:
        for event in pygame.event.get():
            if event.type == QUIT: return
            if event.type == KEYUP and event.key == K_ESCAPE: return
            check_camera_move(event, camera, game_map)
            if event.type == KEYDOWN and event.key == K_UP:
                itm.rotate(1)
                if not game_map.check_collide(itm.get_cell_positions()): itm.rotate(-1)
            if event.type == KEYDOWN and event.key == K_LEFT:
                itm.move(1, 0, 0)
                if not game_map.check_collide(itm.get_cell_positions()): itm.move(-1, 0, 0)
            if event.type == KEYDOWN and event.key == K_RIGHT:
                itm.move(-1, 0, 0)
                if not game_map.check_collide(itm.get_cell_positions()): itm.move(1, 0, 0)
            if event.type == KEYDOWN and event.key == K_DOWN:
                itm.move(0, 0, -1)
                if not game_map.check_collide(itm.get_cell_positions()): itm.move(0, 0, 1)
            if event.type == KEYDOWN and event.key == K_SPACE:
                itm.move(0, 0, -1)
                while game_map.check_collide(itm.get_cell_positions()): itm.move(0, 0, -1)
                itm.move(0, 0, 1)
        '''
        pressed = pygame.key.get_pressed()

        if pressed[K_q]: camera.move(_x=1, _z=0)
        if pressed[K_e]: camera.move(_x=-1, _z=0)
        if pressed[K_w]: camera.move(_x=0, _z=1)
        if pressed[K_s]: camera.move(_x=0, _z=-1)
        if pressed[K_r]: camera.change_view(_a=-1, _r=0)
        if pressed[K_f]: camera.change_view(_a=1, _r=0)
        if pressed[K_a]: camera.change_view(_a=0, _r=-1)
        if pressed[K_d]: camera.change_view(_a=0, _r=1)
        '''
        time_passed = clock.tick(29)
        time_passed_seconds = time_passed / 1000.
        del_seconds += time_passed_seconds

        # Update Game Data ------------------------------------------------------------------------
        game_map.update(time_passed_seconds)
        if game_map.get_type() != GameMap.GT_MATCH:
            ret = itm.update(time_passed_seconds)
            if ret & GameItem.MOVE_END_Z == GameItem.MOVE_END_Z:
                itm.move(0, 0, -1)
                vl = itm.get_cell_positions()
                if not game_map.check_collide(vl):
                    itm.set_dst(itm.dst.x, itm.dst.y, itm.dst.z + 1)
                    game_map.set_item(itm)
                    del_cnt = 1

                    itm = itm.get_next()
                    itm.set_pos(game_map.get_centeri()[0], 0, game_map.get_top() + 4)
                    itm.set_dst(game_map.get_centeri()[0], 0, game_map.get_top())
        if (del_cnt > 0 and del_seconds > 0.5) or (del_seconds > 2.0):
            del_seconds = 0.0
            del_cnt, del_line = game_map.check_match()
            score += del_cnt * game_map.get_type_multi() * del_line

        # Clear the screen, and z-buffer
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        # Reset The Modelview Matrix
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        glEnable(GL_LIGHTING)
        glEnable(GL_DEPTH_TEST)

        # Camera & Light Set ----------------------------------------------------------------------
        camera.look()
        p1, p2 = get_light_pos(camera)
        if light1 is not None:
            # light1.set()
            light1.set_pos([p1.x, p1.y, p1.z])

        # Draw All Object -------------------------------------------------------------------------
        glLibUseShader(shader)

        textures.bind('tex')
        glColor4f(1.0, 1.0, 1.0, 1.0)
        do.glDrawQuad(gcw, -5.0, gch, ('xz', max(gcw, gch) * 3, max(gcw, gch) * 3))
        material_set(MYGL_MATERIAL_CHROME)
        game_map.render(shader)
        if game_map.get_type() != GameMap.GT_MATCH:
            itm.render(shader)
            itm.render_next(shader, game_map.get_centeri()[0], 0, game_map.get_top() + 4)
        glLibUseShader(None)

        # if light1 is not None: light1.draw_as_point()
        if font is not None:
            glColor3f(0.0, 1.0, 0.0)
            DrawText(font, 0, 0, 0, "Score : %d" % score)
        # Show the screen
        pygame.display.flip()
    textures.clear()


if __name__ == '__main__':
    run()
